class Chess_Piece
	attr_reader :color, :x, :y, :dead, :value
	attr_writer :dead, :x, :y
	def initialize( chess_board, xpos, ypos, clr, piece_initial )
		@board = chess_board
		@color = clr
		@x = xpos
		@y = ypos
		@dead = false
		@piece_initial = piece_initial
	end

	#
	# the default attack for most pieces is how it moves
	#
   def attack(xf, yf)
   	move(xf, yf)
   end   

	def as_mnemonic
		"#{@board.color_xlate(@color)}#{@piece_initial}"
	end
	
	#def to_s
	#	name = "#{self.class}"
	#	str = "<img src=\"images/#{self.color[0..0]}_#{name.downcase}.gif\">"
	#	return str
	#end

	#def to_str
	#	name = "#{self.class}"
	#	str = "<img src=\"images/#{self.color[0..0]}_#{name.downcase}.gif\">"
	#	return str
	#end

#-------------------------------------------------------------------------------
# Returns true if this is a valid diagonal move on the given board
#-------------------------------------------------------------------------------
	def diagonal_move( xi, yi, xf, yf, board )
		dx = ( xi - xf ).abs
		dy = ( yi - yf ).abs
	    
		# For a diagonal move, BOTH dx and dx must be > 0 and equal
		raise Chess_Board::NonDiagonalMoveError.new(dx, dy) if (dx == 0 || dy == 0) || (dx != dy)
	    
		# Now for icky conditionals given the direction we travel
		if xi < xf
			$TRACE.debug 5, "checking diagonal movement to the right, from #{xi} to #{xf}"
			if yi < yf
				$TRACE.debug 5, "checking diagonal movement up, from #{yi} to #{yf}"
				dx.times{ |i|
					next if i == 0
					new_x = xi+i
					new_y = yi+i
					$TRACE.debug 9, "checking square, #{new_x}, #{new_y} = #{@board.board[new_x][new_y].class}"
					raise Chess_Board::InterveningPieceError.new(@board, new_x, new_y) unless @board.board[new_x][new_y].nil? 
				}
			else
				$TRACE.debug 5, "checking diagonal movement down, from #{yi} to #{yf}"
				dx.times{ |i| 
					next if i == 0
					new_x = xi+i
					new_y = yi-i
					$TRACE.debug 9, "checking square, #{new_x}, #{new_y} = #{@board.board[new_x][new_y].class}"
					raise Chess_Board::InterveningPieceError.new(@board, new_x, new_y) unless @board.board[new_x][new_y].nil?
				}
			end
		else
			$TRACE.debug 5, "checking diagonal movement to the left, from #{xi} to #{xf}"
			if yi < yf
				$TRACE.debug 5, "checking diagonal movement up, from #{yi} to #{yf}"
				dx.times{ |i|
					next if i == 0
					new_x = xi-i
					new_y = yi+i
					$TRACE.debug 9, "checking square, #{new_x}, #{new_y} = #{@board.board[new_x][new_y].class}"
					raise Chess_Board::InterveningPieceError.new(@board, new_x, new_y) unless @board.board[new_x][new_y].nil?
				}
			else
				$TRACE.debug 5, "checking diagonal movement down, from #{yi} to #{yf}"
				dx.times{ |i| 
					next if i == 0
					new_x = xi-i
					new_y = yi-i
					$TRACE.debug 9, "checking square, #{new_x}, #{new_y} = #{@board.board[new_x][new_y].class}"
					raise Chess_Board::InterveningPieceError.new(@board, new_x, new_y) unless @board.board[new_x][new_y].nil?
				}
			end
		end
		
		# If we got this far, all is well
		return true
	end

	#-------------------------------------------------------------------------------
	# Returns true if this is a valid row or column move on the given board
	#-------------------------------------------------------------------------------
	def row_column_move( xi, yi, xf, yf, board )
		$TRACE.debug 5, "row_column_move from #{xi},#{yi} to #{xf},#{yf}"
		
		dx = ( xi - xf ).abs
		dy = ( yi - yf ).abs
	    
		# Check to make sure it is a row OR a column move
		raise Chess_Board::NonRowColumnMove.new(dx,dy) if dx > 0 && dy > 0
	    
		# Check row moves
		if dx > 0
			if xi < xf
				$TRACE.debug 5, "checking move right along a row #{yi} from #{xi} to #{xf}"
				if dx != 1
					(xi+1).upto( xf-1 ){ |i| 
						$TRACE.debug 9, "checking square, #{i}, #{yf} = #{@board.board[i][yf].class}"
						raise Chess_Board::InterveningPieceError.new(@board, i,yf) unless @board.board[i][yf].nil? 
					}
				end
			else
				$TRACE.debug 5, "checking move left along a row #{yi} from #{xi} to #{xf}"
				if dx != 1
					(xf+1).upto( xi-1 ){ |i| 
						$TRACE.debug 9, "checking square, #{i}, #{yf} = #{@board.board[i][yf].class}"
						raise Chess_Board::InterveningPieceError.new(@board, i,yf) unless @board.board[i][yf].nil?
					}
				end
			end
		# Check column moves
		else
			if yi < yf
				$TRACE.debug 5, "checking move up a column #{xi} from #{yi} to #{yf}"
				if dy != 1
					(yi+1).upto( yf-1 ) { |i| 
						$TRACE.debug 9, "checking square, #{xf}, #{i} = #{@board.board[xf][i].class}"
						raise Chess_Board::InterveningPieceError.new(@board, xf,i) unless @board.board[xf][i].nil? 
					}
				end
			else
				$TRACE.debug 5, "checking move down a column #{xi} from #{yi} to #{yf}"
				if dy != 1
					(yf+1).upto( yi-1 ){ |i| 
						$TRACE.debug 9, "checking square, #{xf}, #{i} = #{@board.board[xf][i].class}"
						raise Chess_Board::InterveningPieceError.new(@board, xf,i) unless @board.board[xf][i].nil? 
					}
				end
			end
		end
		
		# If we got this far, all is well
		return true
	end
end

#-------------------------------------------------------------------------------
# King: Moves any direction one space.
#-------------------------------------------------------------------------------
class King < Chess_Piece	
	attr_reader :moved
	attr_writer :moved
	def initialize( board, xpos, ypos, color, mv="false" )
		super( board, xpos, ypos, color, "k" )
		@value = 10
		mv.strip == "true" ? @moved = true : @moved = false
	end

	def move( xf, yf )
		# Note: Castling is handled by the board
		dx = (@x - xf).abs
		dy = (@y - yf).abs

		raise Chess_Board::IllegalKingMoveError.new(dx, dy) if dx > 1 || dy > 1 # king will _never_ move more than one space up or down

		if self.color == "white"
			opposite_color = "black"
		else
			opposite_color = "white"
		end

		# go through each piece on the board and find all the pieces on the 
		raise Chess_Board::MoveIntoCheckError.new(@board, xf, yf) unless @board.square_attacked_by(xf, yf, @board.opposite_color(self.color)).empty?
		
		@moved = true
		return true
	end
end

#-------------------------------------------------------------------------------
# Queen: Moves any direction any row/column/diagonal any number of spaces.
#-------------------------------------------------------------------------------
class Queen < Chess_Piece
	def initialize( board, xpos, ypos, color )
		super( board, xpos, ypos, color, "q" )
		@value = 9
	end
    
	def move( xf, yf )
		# First, decide if this is a diagonal move or not
		if (@x - xf).abs == (@y - yf).abs # This is diagonal
			diagonal_move( @x, @y, xf, yf, @board )
		else # this is not diagonal
			row_column_move( @x, @y, xf, yf, @board )
		end
	end
end

#-------------------------------------------------------------------------------
# Rook: Moves any row/column in any direction any number of spaces
#-------------------------------------------------------------------------------
class Rook < Chess_Piece
	attr_reader :moved
	attr_writer :moved
	def initialize( board, xpos, ypos, color, mv="false" )
		super( board, xpos, ypos, color, "r" )
		@value = 5
		mv.strip == "true" ? @moved = true : @moved = false
	end
    
	def move( xf, yf )        
		# Check to make sure we have a clear path
		valid = row_column_move( @x, @y, xf, yf, @board )
		@moved = true if valid
		return valid
	end
end

#-------------------------------------------------------------------------------
# Bishop: Moves diagonally along squares, any number of spaces
#-------------------------------------------------------------------------------
class Bishop < Chess_Piece
	def initialize( board, xpos, ypos, color )
		super( board, xpos, ypos, color, "b" )
		@value = 3
	end
    
	def move( xf, yf )
		# Check to make sure there are no pieces in the way
		return diagonal_move( @x, @y, xf, yf, @board )
	end
end

#-------------------------------------------------------------------------------
# Kinght: Moves in L shape. If +/- 1 in X, then +/- 2 in Y, and vice versa
#-------------------------------------------------------------------------------
class Knight < Chess_Piece
	def initialize( board, xpos, ypos, color )
		super( board, xpos, ypos, color, "n" )
		@value = 3
	end
    
	def move( xf, yf )
		dx = ( @x - xf ).abs
		dy = ( @y - yf ).abs
		if dx == 1
		   raise Chess_Board::IllegalKnightMoveError.new(dx,dy) if dy != 2
		elsif dx == 2
		   raise Chess_Board::IllegalKnightMoveError.new(dx,dy) if dy != 1
		else
			raise Chess_Board::IllegalKnightMoveError.new(dx,dy)
		end
	end
end

#-------------------------------------------------------------------------------
# Pawn: Can move only 1 space forward, unless its its first move.  Can only
#       move diagonal if attacking
#-------------------------------------------------------------------------------
class Pawn < Chess_Piece
	attr_accessor :en_passant, :ep_kill
	def initialize( board, xpos, ypos, color, ep=false )
		super( board, xpos, ypos, color, "p" )
		@value = 1
		@en_passant = ep;
		@ep_kill = false;
	end

   def attack(xf, yf)
   	move_internal(xf, yf, true)
   end
   
	def move( xf, yf )
		move_internal(xf, yf, false)
	end
	
	def move_internal(xf, yf, attack_only)
		$TRACE.debug 5, "Pawn(#{@x},#{@y})#move(#{xf},#{yf})"
		dist = (@y - yf).abs
		raise Chess_Board::IllegalPawnMoveDistanceError.new(@board, @x, @y, dist) if dist > 2

		if self.color == "white"
			forward_move = proc{|yf| yf > @y}
			starting_y = 1
			en_passant_y = 5
			behind_adjustment = -1
			opposite_color = "black"
		else
			forward_move = proc{|yf| yf < @y}
			starting_y = 6
			en_passant_y = 2
			behind_adjustment = 1
			opposite_color = "white"
		end

		behind_piece = @board.board[xf][yf+behind_adjustment]
		target_piece = @board.board[xf][yf]

		$TRACE.debug 5, "behind_piece = #{behind_piece}, target_piece = #{target_piece}"
		
		Chess_Board::IllegalPawnMoveBackwardError unless forward_move.call(yf)
		long_move = (@y == starting_y && dist == 2)
		
		# make sure that we are not "jumping" over a piece on a 2 square pawn move
		if long_move
			raise Chess_Board::IllegalPawnLongMovePieceInterveningError.new(@board, @x, @y) if behind_piece
		elsif dist == 2
			# we can only move two when starting from pawn's start position
			raise Chess_Board::IllegalPawnMoveDistanceError.new(@x, @y, dist)
		end

		# Now, we can only allow diagonal if attacking opponent, or doing en-passent
		if @x != xf then
			$TRACE.debug 5, "attempting attack"
		
			if target_piece then
				$TRACE.debug 5, "attempting attack to occupied square"
				if target_piece.color != opposite_color then
					# no taking own piece
					raise "SHOULDN'T GET HERE: can't take own piece"
				end
			else
				$TRACE.debug 5, "attempting attack to empty square"
				if (@board.last_2pawn_move_x == xf && @board.last_2pawn_move_y == en_passant_y + behind_adjustment)
					$TRACE.debug 5, "doing an en-passant"
					# remove behind_piece from board
					@ep_kill = true
				else
					$TRACE.debug 5, "move to empty square error"
					if behind_piece == Pawn && behind_piece.color == opposite_color then
						# no diagonal moves to empty squares unless en passant
						raise Chess_Board::IllegalPawnMoveEnPassantError.new(@board, @x, @y, xf, yf)
					else
						raise Chess_Board::IllegalPawnMoveNoTargetError.new(@board, xf, yf)
					end
			 	end
			end
		else
			if target_piece || attack_only then
				# can't move into occupied square
				Chess_Board::IllegalPawnMoveSquareOccupiedError.new(xf, yf, dist)
			end
		end
			 
=begin		
		# Pawn can only move forward 1 space, or 2 if first move
		if self.color == "white"
			Chess_Board::IllegalPawnMoveBackwardError if yf <= @y # can only move forward!
			@y == 1 && dist == 2 ? long_move = true : long_move = false

			# make sure that we are not "jumping" over a piece on a 2 square pawn move
			if long_move
				raise Chess_Board::IllegalPawnLongMovePieceInterveningError.new(@board, @x, @y) unless @board.board[xf][yf-1].nil?
			elsif dist == 2
				# we can only move two when starting from pawn's start position
				return false
			end
			
			# Now, we can only allow diagonal if attacking opponent, or doing en-passent
			if @x != xf
				# Check for en-passent
				if !@board.board[xf][yf-1].nil? && @board.board[xf][yf-1].color == "black" && @board.board[xf][yf-1].en_passant && yf==5
					# Check to make sure opponent did en-passant last turn
					raise Chess_Board::IllegalPawnMoveEnPassantError.new(@board.xy_xlate(@x, @y)) if @board.board[xf][yf-1].en_passant.to_i != @board.history.size.divmod(2)[1] + @board.history.size.divmod(2)[0]
					@ep_kill = true;
				else
					attack = true
				end
			end
		else
			Chess_Board::IllegalPawnMoveBackwardError if yf >= @y
			@y == 6 && dist == 2 ? long_move = true : long_move = false
			
			# make sure that we are not "jumping" over a piece on a 2 square pawn move
			if long_move
				 raise Chess_Board::IllegalPawnLongMovePieceInterveningError.new(@board, @x, @y) unless @board.board[xf][yf+1].nil?
			elsif dist == 2
				# we can only move two when starting from pawn's start position
				raise Chess_Board::IllegalPawnMoveDistanceError.new(@x, @y, dist)
			end
			
			# Now, we can only allow diagonal if attacking opponent, or doing en-passent
			if @x != xf
				if !@board.board[xf][yf+1].nil? && @board.board[xf][yf+1].color == "white" && @board.board[xf][yf+1].en_passant && yf==2
					# Check to make sure opponent did en-passant last turn
					raise Chess_Board::IllegalPawnMoveEnPassantError.new(@board, @x, @y) if @board.board[xf][yf+1].en_passant.to_i != @board.history.size.divmod(2)[1] + @board.history.size.divmod(2)[0]-1
					@ep_kill = true;
				else
					raise Chess_Board::IllegalPawnMoveNoTargetError.new(@board, xf, yf) if target.nil?
					attack = true
				end
			end
		end
		
		return false unless target.nil? || attack == true
		@en_passant = (dist == 2) ? (@board.history.size.divmod(2)[1] + @board.history.size.divmod(2)[0]) : false
		return true
=end
	end
end
